package com.code2roc.xhood.asm;


import com.code2roc.xhood.util.CommonUtil;
import org.apache.commons.lang3.StringUtils;
import org.objectweb.asm.*;

import java.util.*;

public class ClassInvokeAdapter extends MethodVisitor {
    private LinkedHashMap<String, String> modifyMap;

    public ClassInvokeAdapter(MethodVisitor mv, LinkedHashMap<String, String> modifyMap) {
        super(Opcodes.ASM9, mv);
        this.modifyMap = modifyMap;
    }

    @Override
    public void visitMethodInsn(int opcode, String owner, String name, String descriptor, boolean isInterface) {
        if (owner != null) {
            for (String key : modifyMap.keySet()) {
                owner = CommonUtil.replace(owner, key, modifyMap.get(key));
            }
        }
        if (descriptor != null) {
            for (String key : modifyMap.keySet()) {
                descriptor = CommonUtil.replace(descriptor, key, modifyMap.get(key));
            }
        }
        super.visitMethodInsn(opcode, owner, name, descriptor, isInterface);
    }

    @Override
    public void visitFieldInsn(int opcode, String owner, String name, String descriptor) {
        if (owner != null) {
            for (String key : modifyMap.keySet()) {
                owner = CommonUtil.replace(owner, key, modifyMap.get(key));
            }
        }
        if (descriptor != null) {
            for (String key : modifyMap.keySet()) {
                descriptor = CommonUtil.replace(descriptor, key, modifyMap.get(key));
            }
        }
        super.visitFieldInsn(opcode, owner, name, descriptor);
    }

    @Override
    public void visitLocalVariable(String name, String descriptor, String signature, Label start, Label end, int index) {
        if (descriptor != null) {
            for (String key : modifyMap.keySet()) {
                descriptor = CommonUtil.replace(descriptor, key, modifyMap.get(key));
            }
        }
        if (signature != null) {
            for (String key : modifyMap.keySet()) {
                signature = CommonUtil.replace(signature, key, modifyMap.get(key));
            }
        }
        super.visitLocalVariable(name, descriptor, signature, start, end, index);
    }

    @Override
    public void visitFrame(int type, int numLocal, Object[] local, int numStack, Object[] stack) {
        if (local != null) {
            for (int i = 0; i < local.length; i++) {
                Object object = local[i];
                if (object instanceof String) {
                    String temp = String.valueOf(object);
                    for (String key : modifyMap.keySet()) {
                        temp = CommonUtil.replace(temp, key, modifyMap.get(key));
                    }
                    local[i] = temp;
                }
            }
        }
        if (stack != null) {
            for (int i = 0; i < stack.length; i++) {
                Object object = stack[i];
                if (object instanceof String) {
                    String temp = String.valueOf(object);
                    for (String key : modifyMap.keySet()) {
                        temp = CommonUtil.replace(temp, key, modifyMap.get(key));
                    }
                    stack[i] = temp;
                }
            }
        }
        super.visitFrame(type, numLocal, local, numStack, stack);
    }

    @Override
    public void visitInvokeDynamicInsn(String name, String descriptor, Handle bootstrapMethodHandle, Object... bootstrapMethodArguments) {
        if (name != null) {
            for (String key : modifyMap.keySet()) {
                name = CommonUtil.replace(name, key, modifyMap.get(key));
            }
        }
        if (descriptor != null) {
            for (String key : modifyMap.keySet()) {
                descriptor = CommonUtil.replace(descriptor, key, modifyMap.get(key));
            }
        }
        if (bootstrapMethodArguments != null) {
            for (int i = 0; i < bootstrapMethodArguments.length; i++) {
                Object item = bootstrapMethodArguments[i];
                bootstrapMethodArguments[i] = convertArg(item);
            }
        }
        bootstrapMethodHandle = convertHandle(bootstrapMethodHandle);
        super.visitInvokeDynamicInsn(name, descriptor, bootstrapMethodHandle, bootstrapMethodArguments);
    }

    private Handle convertHandle(Handle bootstrapMethodHandle) {
        if (bootstrapMethodHandle != null) {
            String nameArg = bootstrapMethodHandle.getName();
            if (nameArg != null) {
                for (String key : modifyMap.keySet()) {
                    nameArg = CommonUtil.replace(nameArg, key, modifyMap.get(key));
                }
            }
            String desArg = bootstrapMethodHandle.getDesc();
            if (desArg != null) {
                for (String key : modifyMap.keySet()) {
                    desArg = CommonUtil.replace(desArg, key, modifyMap.get(key));
                }
            }
            String ownerArg = bootstrapMethodHandle.getOwner();
            if (ownerArg != null) {
                for (String key : modifyMap.keySet()) {
                    ownerArg = CommonUtil.replace(ownerArg, key, modifyMap.get(key));
                }
            }
            bootstrapMethodHandle = new Handle(bootstrapMethodHandle.getTag(), ownerArg, nameArg, desArg, bootstrapMethodHandle.isInterface());
        }
        return bootstrapMethodHandle;
    }

    private Type convertType(Type type) {
        String descriptor = type.getDescriptor();
        if (descriptor != null) {
            for (String key : modifyMap.keySet()) {
                descriptor = CommonUtil.replace(descriptor, key, modifyMap.get(key));
            }
            return Type.getType(descriptor);
        }
        return type;
    }

    private Object convertArg(Object arg) {
        if (arg instanceof Handle) {
            return convertHandle((Handle) arg);
        } else if (arg instanceof Type) {
            return convertType((Type) arg);
        } else {
            return arg;
        }
    }

    @Override
    public AnnotationVisitor visitAnnotation(String descriptor, boolean visible) {
        if (descriptor != null) {
            for (String key : modifyMap.keySet()) {
                descriptor = CommonUtil.replace(descriptor, key, modifyMap.get(key));
            }
        }
        AnnotationVisitor annotationVisitor = super.visitAnnotation(descriptor, visible);
        return new ClassAnnotationAdapter(annotationVisitor, modifyMap);
    }

    @Override
    public AnnotationVisitor visitTypeAnnotation(int typeRef, TypePath typePath, String descriptor, boolean visible) {
        if (descriptor != null) {
            for (String key : modifyMap.keySet()) {
                descriptor = CommonUtil.replace(descriptor, key, modifyMap.get(key));
            }
        }
        AnnotationVisitor annotationVisitor = super.visitTypeAnnotation(typeRef, typePath, descriptor, visible);
        return new ClassAnnotationAdapter(annotationVisitor, modifyMap);
    }

    @Override
    public AnnotationVisitor visitTryCatchAnnotation(int typeRef, TypePath typePath, String descriptor, boolean visible) {
        if (descriptor != null) {
            for (String key : modifyMap.keySet()) {
                descriptor = CommonUtil.replace(descriptor, key, modifyMap.get(key));
            }
        }
        AnnotationVisitor annotationVisitor = super.visitTryCatchAnnotation(typeRef, typePath, descriptor, visible);
        return new ClassAnnotationAdapter(annotationVisitor, modifyMap);
    }

    @Override
    public AnnotationVisitor visitParameterAnnotation(int parameter, String descriptor, boolean visible) {
        if (descriptor != null) {
            for (String key : modifyMap.keySet()) {
                descriptor = CommonUtil.replace(descriptor, key, modifyMap.get(key));
            }
        }
        AnnotationVisitor annotationVisitor = super.visitParameterAnnotation(parameter, descriptor, visible);
        return new ClassAnnotationAdapter(annotationVisitor, modifyMap);
    }

    @Override
    public AnnotationVisitor visitLocalVariableAnnotation(int typeRef, TypePath typePath, Label[] start, Label[] end, int[] index, String descriptor, boolean visible) {
        if (descriptor != null) {
            for (String key : modifyMap.keySet()) {
                descriptor = CommonUtil.replace(descriptor, key, modifyMap.get(key));
            }
        }
        AnnotationVisitor annotationVisitor = super.visitLocalVariableAnnotation(typeRef, typePath, start, end, index, descriptor, visible);
        return new ClassAnnotationAdapter(annotationVisitor, modifyMap);
    }

    @Override
    public AnnotationVisitor visitInsnAnnotation(int typeRef, TypePath typePath, String descriptor, boolean visible) {
        if (descriptor != null) {
            for (String key : modifyMap.keySet()) {
                descriptor = CommonUtil.replace(descriptor, key, modifyMap.get(key));
            }
        }
        AnnotationVisitor annotationVisitor = super.visitInsnAnnotation(typeRef, typePath, descriptor, visible);
        return new ClassAnnotationAdapter(annotationVisitor, modifyMap);
    }

    @Override
    public AnnotationVisitor visitAnnotationDefault() {
        AnnotationVisitor annotationVisitor = super.visitAnnotationDefault();
        return new ClassAnnotationAdapter(annotationVisitor, modifyMap);
    }

    @Override
    public void visitMultiANewArrayInsn(String descriptor, int numDimensions) {
        if (descriptor != null) {
            for (String key : modifyMap.keySet()) {
                descriptor = CommonUtil.replace(descriptor, key, modifyMap.get(key));
            }
        }
        super.visitMultiANewArrayInsn(descriptor, numDimensions);
    }

    @Override
    public void visitTryCatchBlock(Label start, Label end, Label handler, String type) {
        if (type != null) {
            for (String key : modifyMap.keySet()) {
                type = CommonUtil.replace(type, key, modifyMap.get(key));
            }
        }
        super.visitTryCatchBlock(start, end, handler, type);
    }

    @Override
    public void visitTypeInsn(int opcode, String type) {
        if (type != null) {
            for (String key : modifyMap.keySet()) {
                type = CommonUtil.replace(type, key, modifyMap.get(key));
            }
        }
        super.visitTypeInsn(opcode, type);
    }

    @Override
    public void visitLdcInsn(Object value) {
        if (value instanceof Type) {
            Type obj = (Type) value;
            String desc = obj.getDescriptor();
            if (desc != null) {
                for (String key : modifyMap.keySet()) {
                    desc = CommonUtil.replace(desc, key, modifyMap.get(key));
                }
                value = Type.getType(desc);
            }
        }
        super.visitLdcInsn(value);
    }

}
